﻿using System.Diagnostics;
using System.Linq;
using NHDay_Linq.Domain;
using NUnit.Framework;
using NHibernate.Linq;

namespace NHDay_Linq
{
	[TestFixture]
	public class T01_TransactionsTests : TestBase
	{
		/// <summary>
		/// this test will succeed, because NHib is forced to hit the database immediately on the save
		/// (it's a native key)
		/// </summary>
		[Test]
		public void T01_EntityId_WithoutTransaction()
		{
			using (var session = SessionFactory.OpenSession())
			{
				var item = new EntityId();
				item.Name = "Test";
				session.Save(item);

				var entryFromRepo = session.Query<EntityId>().Single(x => x.Id == item.Id);

				Assert.AreEqual(item, entryFromRepo);
			}
		}

		/// <summary>
		/// this test will fail, NHibernate isn't forced to execute the save immediately due to the use of implicit
		/// transaction
		/// </summary>
		[Test]
		public void T02_EntityGuid_WithoutTransaction()
		{
			using (var session = SessionFactory.OpenSession())
			{
				var item = new EntityGuid();
				item.Id = System.Guid.NewGuid();
				item.Name = "Test";
				session.Save(item);

				var entryFromRepo = session.Query<EntityGuid>().Single(x => x.Id == item.Id);

				Assert.AreEqual(item, entryFromRepo);
			}
		}

		/// <summary>
		///	Using an explicit transaction forces NHibernate to behave the correct way:
		/// we are are enforcing the operation to be execute in the correct sequence
		/// </summary>
		[Test]
		public void T03_EntityGuid_WithoutTransaction_Fixed()
		{
			using (var session = SessionFactory.OpenSession())
			using (var tx = session.BeginTransaction())
			{
				var item = new EntityGuid();
                item.Id = System.Guid.NewGuid();
				item.Name = "Test";
				session.Save(item);

				var entryFromRepo = session.Query<EntityGuid>().Single(x => x.Id == item.Id);

				Assert.AreEqual(item, entryFromRepo);
				tx.Commit();
			}
		}

		/// <summary>
		///	Repo pattern + Linq + transactions, repo managed session
		/// </summary>
		[Test]
		public void T04_Repository_Transaction_SessionTiedToRepository()
		{
			var item = new EntityGuid();
			IQueryable<EntityGuid> entriesFromRepo;
			
			using (var repo = new Repository.Repository<EntityGuid>(SessionFactory))
			{
				Debug.WriteLine("Starting transaction");
				repo.BeginTransaction();

				item.Name = "Test";
				repo.Save(item);

				// do other things...
				
				entriesFromRepo = repo.Query().Where(x => x.Id == item.Id);

				Debug.WriteLine("Transaction Commit");
			}

			// perform some more 'In memory elaborations' again using linq after retrieving your data
			
			Debug.WriteLine("Performing In memory elaboration with linq");
			
			var result = entriesFromRepo.Select(e => new {Loaded = true, Name = e.Name, OriginalId = e.Id}).Single();
			// oops we made a mistake and we are executing things outside the transaction.
			// we get an exception: session is closed!
			
			Assert.IsNotNull(result);
		}

		/// <summary>
		///	Repo pattern + Linq + transactions, external session.
		/// 
		/// the test succeed but the queries can be executed outside the transaction
		/// </summary>
		[Test]
		public void T05_Repository_Transaction_ExternalSession()
		{
			var item = new EntityGuid();
			IQueryable<EntityGuid> entriesFromRepo;

			using (var session = SessionFactory.OpenSession())
			{
				using (var repo = new Repository.Repository<EntityGuid>(session))
				{
					Debug.WriteLine("Starting transaction");
					repo.BeginTransaction();

					item.Name = "Test";
					repo.Save(item);

					// do other things...

					entriesFromRepo = repo.Query().Where(x => x.Id == item.Id);

					Debug.WriteLine("Transaction Commit");
				}

				// perform some more 'In memory elaborations' again using linq after retrieving your data

				Debug.WriteLine("Performing In memory elaboration with linq");

				var result = entriesFromRepo.Select(e => new {Loaded = true, Name = e.Name, OriginalId = e.Id}).Single();
				// oops we made a mistake and we are executing things outside the transaction (look at the tracing).
				
				Assert.IsNotNull(result);
			}
		}

	}
}
